package uk.me.dewi.appenin.android.dao.rest.xml;

import static uk.me.dewi.util.DateFormats.ISO_DATE_TIME_MILLIS_FORMAT;

import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.text.ParseException;
import java.util.ArrayList;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlPullParserFactory;
import org.xmlpull.v1.XmlSerializer;

import uk.me.dewi.appenin.android.dao.PlaceDao;
import uk.me.dewi.appenin.android.dao.rest.PlaceRestDao;
import uk.me.dewi.appenin.android.entity.Place;
import uk.me.dewi.net.rest.ResponseHandler;
import uk.me.dewi.net.rest.RestException;
import android.util.Log;

public class PlaceXmlRestDao extends PlaceRestDao implements PlaceDao {

    private final XmlPullParserFactory mFactory;
    
    public PlaceXmlRestDao(String baseUrl){
        super(new XmlRequestFactory(baseUrl));
        try {
            mFactory = XmlPullParserFactory.newInstance();
        } catch (XmlPullParserException e) {
            throw new RuntimeException("Could not create XmlPullParserException", e);
        }
    }

    public Place createPlace(String path, Place place) throws RestException {
        return (Place) mRestClient.put(path, toXml(place), new ResponseHandler(){
            public Object handleResponse(String resultString) throws Exception{
                StringReader reader = new StringReader(resultString);
                XmlPullParser parser = mFactory.newPullParser();
                parser.setInput(reader);
                return populate(new Place(), parser);
            }
        });
    }

    /* (non-Javadoc)
     * @see uk.me.dewi.appenin.android.AppeninRestAdapter#deletePlace(long)
     */
    public boolean deletePlace(String path) throws RestException {
        throw new RuntimeException("NOT IMPLEMENTED");
    }

    /* (non-Javadoc)
     * @see uk.me.dewi.appenin.android.AppeninRestAdapter#getNearbyPlaces(com.google.android.maps.GeoPoint, int)
     */
    @SuppressWarnings("unchecked")
    public ArrayList<Place> getNearbyPlaces(String path) throws RestException {
        return (ArrayList<Place>) mRestClient.get(path, new ResponseHandler() {
            public Object handleResponse(String result) throws Exception{
                XmlPullParser parser = mFactory.newPullParser();
                parser.setInput(new StringReader(result));
                return loadPlaces(parser);
            }
        });
    }
    
    /* (non-Javadoc)
     * @see uk.me.dewi.appenin.android.AppeninRestAdapter#getNearbyPlaceIDs(com.google.android.maps.GeoPoint, int)
     */
    public long[] getNearbyPlaceIDs(String path) throws RestException {
        throw new RuntimeException("NOT IMPLEMENTED");
    }

    /* (non-Javadoc)
     * @see uk.me.dewi.appenin.android.AppeninRestAdapter#fetchPlace(long)
     */
    public Place fetchPlace(String path) throws RestException {
        return (Place) mRestClient.get(path, new ResponseHandler(){
            public Object handleResponse(String result) throws Exception{
                XmlPullParser parser = mFactory.newPullParser();
                StringReader reader = new StringReader(result);
                parser.setInput(reader);
                return populate(new Place(), parser).validate();
            }
        });
    }
    
    public ArrayList<Place> loadPlaces(XmlPullParser parser) 
                                    throws XmlPullParserException, IOException, RestException{
        ArrayList<Place> places = new ArrayList<Place>();
        if (parser.nextTag() == XmlPullParser.START_TAG
                && parser.getName().equals("streaming-query-result")) {
            while (parser.nextTag() != XmlPullParser.END_TAG) {
                places.add(populate(new Place(), parser));
            }
        }else{
            throw new RestException("Places Element Required");
        }
        return places;
    }

    /* (non-Javadoc)
     * @see uk.me.dewi.appenin.android.AppeninRestAdapter#updatePlace(uk.me.dewi.appenin.android.entity.Place)
     */
    public boolean updatePlace(String path, Place place) throws RestException {
        throw new RuntimeException("NOT IMPLEMENTED");
    }
    

    /**
     *<Place>
        <ratings>10</ratings>
        <description>This is some place</description>
        <tags>some place</tags>
        <created>2009-05-26T21:25:41.204466</created>
        <modified>2009-05-26T21:25:41.205207</modified>
        <user>jinglemansweep@googlemail.com</user>
        <geo>51.0,1.1</geo>
        <name>Some Place</name>
      </Place>
     * @param parser
     * @throws XmlPullParserException
     * @throws IOException
     */
    public Place populate(Place place, XmlPullParser parser) throws XmlPullParserException, IOException {

        if(parser.getEventType() == parser.START_DOCUMENT) {
            parser.nextTag();
        }

        if ((parser.getName() != null && parser.getName().equals("place"))
                || (parser.nextTag() == XmlPullParser.START_TAG
                    && parser.getName().equals("place"))) {
            
            while (parser.nextTag() != XmlPullParser.END_TAG) {
                if (parser.getName().equals("id")) {
                    place.mId = Long.parseLong(parser.nextText());
                } else if (parser.getName().equals("ratings")) {
                    place.mRatings = Integer.parseInt(parser.nextText());
                } else if (parser.getName().equals("description")) {
                    place.mDescription = parser.nextText();
                } else if (parser.getName().equals("tags")) {
                    place.mTags = parser.nextText().split(" ");
                } else if (parser.getName().equals("date-added")) {
                    try {
                        place.mCreated = ISO_DATE_TIME_MILLIS_FORMAT.parse(parser.nextText());
                    } catch (ParseException e) {
                        e.printStackTrace();
                    }
                } else if (parser.getName().equals("modified")) {
                    try {
                        place.mModified = ISO_DATE_TIME_MILLIS_FORMAT.parse(parser.nextText());
                    } catch (ParseException e) {
                        e.printStackTrace();
                    }
                } else if (parser.getName().equals("user")) {
                    place.mUser = parser.nextText();
                } else if (parser.getName().equals("location")) {
                    Log.w("PlaceXmlRestDao", "location (not implemented) :"+parser.nextText());
                } else if (parser.getName().equals("name")) {
                    place.mName = parser.nextText();
                }
            }

//            parser.nextTag();
            parser.require(XmlPullParser.END_TAG, null, "place");
            return place;
        } else {
            throw new RuntimeException("Expected Place XML at" + parser.getPositionDescription());
        }

    }
    
    /**
     * Converts this Place into an XML String, e.g.:
      <Place>
        <ratings>10</ratings>
        <description>This is some place</description>
        <tags>some place</tags>
        <created>2009-05-26T21:25:41.204466</created>
        <modified>2009-05-26T21:25:41.205207</modified>
        <user>jinglemansweep@googlemail.com</user>
        <geo>51.0,1.1</geo>
        <name>Some Place</name>
      </Place>
    **/
    public String toXml(Place place){
        try{
            XmlSerializer writer = mFactory.newSerializer();
            StringWriter stringWriter = new StringWriter();
            writer.setOutput(stringWriter);
            
            writer.startTag(null, "place")
                .attribute(null, "_new", Boolean.toString(place.isNew()))
            .startTag(null, "location")
                .attribute(null, "latitude-e6", Integer.toString(place.mGeo.getLatitudeE6()))
                .attribute(null, "longitude-e6", Integer.toString(place.mGeo.getLongitudeE6()))
            .endTag(null, "location")
            .startTag(null, "date-added").text(ISO_DATE_TIME_MILLIS_FORMAT.format(place.mCreated)).endTag(null, "date-added")
            .startTag(null, "name").text(place.mName).endTag(null, "name")
            .startTag(null, "description").text(place.mDescription).endTag(null, "description");
            if(place.mId != null){
                writer.startTag(null, "id").text(place.mId.toString()).endTag(null, "id");
            }
//            .startTag(null, "ratings").text(""+place.mRatings).endTag(null, "ratings")
//            .startTag(null, "tags").text(ArrayUtils.join(place.mTags, " ")).endTag(null, "tags")
//            
//            .startTag(null, "modified").text(ISO_DATE_TIME_MILLIS_FORMAT.format(place.mModified)).endTag(null, "modified")
//            .startTag(null, "user").text(place.mUser).endTag(null, "user")
//            .startTag(null, "geo").text("0,0").endTag(null, "geo")
//            ;
    
            writer.endTag(null, "place");
            writer.flush();
            String result = stringWriter.toString();
            Log.d("PlaceXmlRestDao", result);
            return result;
        } catch (Exception e) {
            throw new RuntimeException("Could not convert "+toString()+" to XML", e);
        }
    }
    
    
//    
//    /**
//     *<Place>
//        <ratings>10</ratings>
//        <description>This is some place</description>
//        <tags>some place</tags>
//        <created>2009-05-26T21:25:41.204466</created>
//        <modified>2009-05-26T21:25:41.205207</modified>
//        <user>jinglemansweep@googlemail.com</user>
//        <geo>51.0,1.1</geo>
//        <name>Some Place</name>
//      </Place>
//     * @param parser
//     * @throws XmlPullParserException
//     * @throws IOException
//     */
//    public Place populate(Place place, XmlPullParser parser) throws XmlPullParserException, IOException {
//        if (parser.nextTag() == XmlPullParser.START_TAG
//                && parser.getName().equals("Place")) {
//            
//            while (parser.nextTag() != XmlPullParser.END_TAG) {
//                if (parser.getName().equals("ratings")) {
//                    place.mRatings = Integer.parseInt(parser.nextText());
//                } else if (parser.getName().equals("description")) {
//                    place.mDescription = parser.nextText();
//                } else if (parser.getName().equals("tags")) {
//                    place.mTags = parser.nextText().split(" ");
//                } else if (parser.getName().equals("created")) {
//                    try {
//                        place.mCreated = ISO_DATE_TIME_MILLIS_FORMAT.parse(parser.nextText());
//                    } catch (ParseException e) {
//                        e.printStackTrace();
//                    }
//                } else if (parser.getName().equals("modified")) {
//                    try {
//                        place.mModified = ISO_DATE_TIME_MILLIS_FORMAT.parse(parser.nextText());
//                    } catch (ParseException e) {
//                        e.printStackTrace();
//                    }
//                } else if (parser.getName().equals("user")) {
//                    place.mUser = parser.nextText();
//                } else if (parser.getName().equals("geo")) {
//                    place.mGeo = parseGeoString(parser.nextText());
//                } else if (parser.getName().equals("name")) {
//                    place.mName = parser.nextText();
//                }
//            }
//
//            parser.nextTag();
//            parser.require(XmlPullParser.END_TAG, null, "rss");
//            return place;
//        } else {
//            throw new RuntimeException("Expected Place XML at" + parser.getPositionDescription());
//        }
//
//    }
//    
//    /**
//     * Converts this Place into an XML String, e.g.:
//      <Place>
//        <ratings>10</ratings>
//        <description>This is some place</description>
//        <tags>some place</tags>
//        <created>2009-05-26T21:25:41.204466</created>
//        <modified>2009-05-26T21:25:41.205207</modified>
//        <user>jinglemansweep@googlemail.com</user>
//        <geo>51.0,1.1</geo>
//        <name>Some Place</name>
//      </Place>
//    **/
//    public String toXml(Place place){
//        try{
//            XmlSerializer writer = mFactory.newSerializer();
//            StringWriter stringWriter = new StringWriter();
//            writer.setOutput(stringWriter);
//            
//            writer.startTag(null, "Place")
//            .startTag(null, "ratings").text(""+place.mRatings).endTag(null, "ratings")
//            .startTag(null, "description").text(place.mDescription).endTag(null, "description")
//            .startTag(null, "tags").text(ArrayUtils.join(place.mTags, " ")).endTag(null, "tags")
//            .startTag(null, "created").text(ISO_DATE_TIME_MILLIS_FORMAT.format(place.mCreated)).endTag(null, "created")
//            .startTag(null, "modified").text(ISO_DATE_TIME_MILLIS_FORMAT.format(place.mModified)).endTag(null, "modified")
//            .startTag(null, "user").text(place.mUser).endTag(null, "user")
//            .startTag(null, "geo").text(place.getGeoString()).endTag(null, "geo")
//            .startTag(null, "name").text(place.mName).endTag(null, "name");
//    
//            writer.endTag(null, "Place");
//            
//            return stringWriter.toString();
//        } catch (Exception e) {
//            throw new RuntimeException("Could not convert "+toString()+" to XML", e);
//        }
//    }
}
